﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
using CoreEx.Common.Extensions;
using CoreEx.Database.Query.Expressions;
using LinFu.IoC.Configuration;

namespace CoreEx.Database.Linq.Implementation
{
    [Implements(typeof(ISubQueryTranslator))]
    public class SubQueryTranslator : QueryTranslator, ISubQueryTranslator
    {
        private TranslateResult _outerTranslateResult;
        
        public SubQueryTranslator(string connectionName) : base(connectionName)
        {
        }

        public TranslateResult Translate(MethodCallExpression methodCallExpression, TranslateResult outerTranslateResult)
        {
            _outerTranslateResult = outerTranslateResult;
            Visit(methodCallExpression);
            TranslateResult.SelectQuery.QueryExpression.IsSubQuery = true;
            return TranslateResult;
        }



        protected override TableSource GetOuterTableSourceForMemberExpression(MemberExpression memberExpression)
        {
            if (memberExpression.Expression.NodeType == ExpressionType.Parameter)
                return _outerTranslateResult.TableSources[memberExpression.Expression.Type];
            return base.GetOuterTableSourceForMemberExpression(memberExpression);
        }

        private DbExpression CreateWhereExpression(MemberExpression memberExpression, TableSource outerTableSource, string alias)
        {
            var outerAlias = outerTableSource.Alias;
            var outerPropertyMappingInfo = outerTableSource.MappedProperties[(PropertyInfo)memberExpression.Member].PropertyMappingInfo;
            var foreignKeyInfo = outerPropertyMappingInfo.ForeignKeyInfo;
            
            DbExpression whereCriteraExpression = null;

            foreach (var foreignkeyColumnInfo in foreignKeyInfo.Columns)
            {
                DbExpression equalsExpression = null;

                equalsExpression = ExpressionFactory.Column(outerAlias, foreignkeyColumnInfo.PrimaryColumnInfo.ColumnName) ==
                                       ExpressionFactory.Column(alias, foreignkeyColumnInfo.ForeignColumnInfo.ColumnName);


                if (whereCriteraExpression.IsNull())
                    whereCriteraExpression = equalsExpression;
                else
                    whereCriteraExpression = whereCriteraExpression & equalsExpression;
                
            }

            TranslateResult.SelectQuery.Where(whereCriteraExpression);

            return whereCriteraExpression;
        }

        protected override DbExpression CreateJoinExpression(MemberExpression memberExpression, TableSource outerTableSource, string alias)
        {
            if (ExpressionRefersToOuterTranslateResult(memberExpression.Expression))
                return CreateWhereExpression(memberExpression, outerTableSource, alias);
            return base.CreateJoinExpression(memberExpression, outerTableSource, alias);
        }


        protected override TableSource CreateTableSourceForParameterExpression(ParameterExpression expression)
        {
            if (ExpressionRefersToOuterTranslateResult(expression))
                return _outerTranslateResult.TableSources[expression.Type];
            return base.CreateTableSourceForParameterExpression(expression);
        }


        private bool ExpressionRefersToOuterTranslateResult(Expression containingExpression)
        {
            return (_outerTranslateResult.RootTableSource.TargetMember == containingExpression.Type); 
        }


        protected override DbExpression VisitMemberAccess(MemberExpression expression)
        {
            if (expression.Type.IsEnumerable())
            {
                //TODO Rename this to something like CreateJoinsNeededToAccessMember
                GetTableSource((Expression)expression);

                return TranslateResult.SelectQuery;
            }
            return base.VisitMemberAccess(expression);
        }
            
    }
}
